home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************
- * check_soa -- Retrieve the SOA record from each name server *
- * for a given domain and print out the serial number. *
- * *
- * usage: check_soa domain *
- * *
- * The following errors are reported: *
- * o There is no address for a server. *
- * o There is no server running on this host. *
- * o There was no response from a server. *
- * o The server is not authoritative for the domain. *
- * o The response had an error response code. *
- * o The response had more than one answer. *
- * o The response answer did not contain an SOA record. *
- * o The expansion of a compressed domain name failed. *
- ****************************************************************/
-
- /* Various header files */
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <arpa/inet.h>
- #include <arpa/nameser.h>
- #include <resolv.h>
- #include <string.h>
-
- /* Error variables */
- extern int h_errno; /* for resolver errors */
- extern int errno; /* general system errors */
-
- #ifndef __BIT_TYPES_DEFINED__
- /* typedef from BIND source file compat/include/sys/bitypes.h */
- typedef unsigned int u_int32_t;
- #endif /* __BIT_TYPES_DEFINED__ */
-
- /* Our own routines; code included later in this chapter */
- void nsError(); /* report resolver errors */
- void findNameServers(); /* find a domain's name servers */
- void queryNameServers(); /* grab SOA records from servers */
- void returnCodeError(); /* report response packet errors */
- int skipToData(); /* skip to the resource record data */
- int skipName(); /* skip a compressed name */
-
- /* Maximum number of name servers we will check */
- #define NSLIMIT 20
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- char *nsList[NSLIMIT]; /* list of name servers */
- int nsNum = 0; /* number of name servers in list */
-
- /* sanity check: one (and only one) argument? */
- if(argc != 2){
- (void) fprintf(stderr, "usage: %s domain\n", argv[0]);
- exit(1);
- }
-
- (void) res_init();
-
- /*
- * Find the name servers for the domain.
- * The name servers are written into nsList.
- */
- findNameServers(argv[1], nsList, &nsNum);
-
- /*
- * Query each name server for the domain's SOA record.
- * The name servers are read from nsList.
- */
- queryNameServers(argv[1], nsList, nsNum);
-
- exit(0);
- }
-
- /****************************************************************
- * findNameServers -- find all of the name servers for the *
- * given domain and store their names in nsList. nsNum is *
- * the number of servers in the nsList array. *
- ****************************************************************/
- void
- findNameServers(domain, nsList, nsNum)
- char *domain;
- char *nsList[];
- int *nsNum;
- {
- union {
- HEADER hdr; /* defined in resolv.h */
- u_char buf[PACKETSZ]; /* defined in arpa/nameser.h */
- } response; /* response buffers */
- int responseLen; /* buffer length */
-
- u_char *cp; /* character pointer to parse DNS packet */
- u_char *endOfMsg; /* need to know the end of the message */
- u_short class; /* classes defined in arpa/nameser.h */
- u_short type; /* types defined in arpa/nameser.h */
- u_int32_t ttl; /* resource record time to live */
- u_short dlen; /* size of resource record data */
-
- int i, count, dup; /* misc variables */
-
- /*
- * Look up the NS records for the given domain name.
- * We expect the domain to be a fully qualified name, so
- * we use res_query(). If we wanted the resolver search
- * algorithm, we would have used res_search() instead.
- */
- if((responseLen =
- res_query(domain, /* the domain we care about */
- C_IN, /* Internet class records */
- T_NS, /* Look up name server records*/
- (u_char *)&response, /*response buffer*/
- sizeof(response))) /*buffer size */
- < 0){ /*If negative */
- nsError(h_errno, domain); /* report the error */
- exit(1); /* and quit */
- }
-
- /*
- * Keep track of the end of the message so we don't
- * pass it while parsing the response. responseLen is
- * the value returned by res_query.
- */
- endOfMsg = response.buf + responseLen;
-
- /*
- * Set a pointer to the start of the question section,
- * which begins immediately AFTER the header.
- */
- cp = response.buf + sizeof(HEADER);
-
- /*
- * Skip over the whole question section. The question
- * section is comprised of a name, a type, and a class.
- * QFIXEDSZ (defined in arpa/nameser.h) is the size of
- * the type and class portions, which is fixed. Therefore,
- * we can skip the question section by skipping the
- * name (at the beginning) and then advancing QFIXEDSZ.
- * After this calculation, cp points to the start of the
- * answer section, which is a list of NS records.
- */
- cp += skipName(cp, endOfMsg) + QFIXEDSZ;
-
- /*
- * Create a list of name servers from the response.
- * NS records may be in the answer section and/or in the
- * authority section depending on the DNS implementation.
- * Walk through both. The name server addresses may be in
- * the additional records section, but we will ignore them
- * since it is much easier to call gethostbyname() later
- * than to parse and store the addresses here.
- */
- count = ntohs(response.hdr.ancount) +
- ntohs(response.hdr.nscount);
- while ( (--count >= 0) /* still more records */
- && (cp < endOfMsg) /* still inside the packet*/
- && (*nsNum < NSLIMIT)) { /* still under our limit */
-
- /* Skip to the data portion of the resource record */
- cp += skipToData(cp, &type, &class, &ttl, &dlen, endOfMsg);
-
- if (type == T_NS) {
-
- /*
- * Allocate storage for the name. Like any good
- * programmer should, we test malloc's return value,
- * and quit if it fails.
- */
- nsList[*nsNum] = (char *) malloc (MAXDNAME);
- if(nsList[*nsNum] == NULL){
- (void) fprintf(stderr, "malloc failed\n");
- exit(1);
- }
-
- /* Expand the name server's name */
- if (dn_expand(response.buf, /* Start of the packet */
- endOfMsg, /* End of the packet */
- cp, /* Position in the packet*/
- (u_char *)nsList[*nsNum], /* Result */
- MAXDNAME) /* size of nsList buffer */
- < 0) { /* Negative: error */
- (void) fprintf(stderr, "dn_expand failed\n");
- exit(1);
- }
-
- /*
- * Check the name we've just unpacked and add it to
- * the list of servers if it is not a duplicate.
- * If it is a duplicate, just ignore it.
- */
- for(i = 0, dup=0; (i < *nsNum) && !dup; i++)
- dup = !strcasecmp(nsList[i], nsList[*nsNum]);
- if(dup)
- free(nsList[*nsNum]);
- else
- (*nsNum)++;
- }
-
- /* Advance the pointer over the resource record data */
- cp += dlen;
-
- } /* end of while */
- }
-
- /****************************************************************
- * queryNameServers -- Query each of the name servers in nsList *
- * for the SOA record of the given domain. Report any *
- * errors encountered. (e.g., a name server not running or *
- * the response not being an authoritative response.) If *
- * there are no errors, print out the serial number for the *
- * domain. *
- ****************************************************************/
- void
- queryNameServers(domain, nsList, nsNum)
- char *domain;
- char *nsList[];
- int nsNum;
- {
- union {
- HEADER hdr; /* defined in resolv.h */
- u_char buf[PACKETSZ]; /* defined in arpa/nameser.h */
- } query, response; /* query and response buffers */
- int responseLen, queryLen; /* buffer lengths */
-
- u_char *cp; /* character pointer to parse DNS packet */
- u_char *endOfMsg; /* need to know the end of the message */
- u_short class; /* classes defined in arpa/nameser.h */
- u_short type; /* types defined in arpa/nameser.h */
- u_int32_t ttl; /* resource record time to live */
- u_short dlen; /* size of resource record data */
- u_int32_t serial; /* SOA serial number */
-
- struct in_addr saveNsAddr[MAXNS]; /* addrs saved from _res */
- int nsCount; /* count of addresses saved from _res */
- struct hostent *host; /* structure for looking up ns addr */
- int i; /* counter variable */
-
- /*
- * Save the _res name server list since
- * we will need to restore it later.
- */
- nsCount = _res.nscount;
- for(i = 0; i < nsCount; i++)
- saveNsAddr[i] = _res.nsaddr_list[i].sin_addr;
-
- /*
- * Turn off the search algorithm and turn off appending
- * the default domain before we call gethostbyname(); the
- * name server names will be fully qualified.
- */
- _res.options &= ~(RES_DNSRCH | RES_DEFNAMES);
-
- /*
- * Query each name server for an SOA record.
- */
- for(nsNum-- ; nsNum >= 0; nsNum--){
-
- /*
- * First, we have to get the IP address of every server.
- * So far, all we have are names. We use gethostbyname
- * to get the addresses, rather than anything fancy.
- * But first, we have to restore certain values in _res
- * because _res affects gethostbyname(). (We altered
- * _res in the previous iteration through the loop.)
- *
- * We can't just call res_init() again to restore
- * these values since some of the _res fields are
- * initialized when the variable is declared, not when
- * res_init() is called.
- */
- _res.options |= RES_RECURSE; /* recursion on (default) */
- _res.retry = 4; /* 4 retries (default) */
- _res.nscount = nsCount; /* original name servers */
- for(i = 0; i < nsCount; i++)
- _res.nsaddr_list[i].sin_addr = saveNsAddr[i];
-
- /* Look up the name server's address */
- host = gethostbyname(nsList[nsNum]);
- if (host == NULL) {
- (void) fprintf(stderr,"There is no address for %s\n",
- nsList[nsNum]);
- continue; /* nsNum for-loop */
- }
-
- /*
- * Now get ready for the real fun. host contains IP
- * addresses for the name server we're testing.
- * Store the first address for host in the _res
- * structure. Soon, we'll look up the SOA record...
- */
- (void) memcpy((void *)&_res.nsaddr_list[0].sin_addr,
- (void *)host->h_addr_list[0], (size_t)host->h_length);
- _res.nscount = 1;
-
- /*
- * Turn off recursion. We don't want the name server
- * querying another server for the SOA record; this name
- * server ought to be authoritative for this data.
- */
- _res.options &= ~RES_RECURSE;
-
- /*
- * Reduce the number of retries. We may be checking
- * several name servers, so we don't want to wait too
- * long for any one server. With two retries and only
- * one address to query, we'll wait at most 15 seconds.
- */
- _res.retry = 2;
-
- /*
- * We want to see the response code in the next
- * response, so we must make the query packet and
- * send it ourselves instead of having res_query()
- * do it for us. If res_query() returned -1, there
- * might not be a response to look at.
- *
- * There is no need to check for res_mkquery()
- * returning -1. If the compression was going to
- * fail, it would have failed when we called
- * res_query() earlier with this domain name.
- */
- queryLen = res_mkquery(
- QUERY, /* regular query */
- domain, /* the domain to look up */
- C_IN, /* Internet type */
- T_SOA, /* Look up an SOA record */
- (char *)NULL, /* always NULL */
- 0, /* length of NULL */
- (struct rrec *)NULL, /* always NULL */
- (char *)&query, /* buffer for the query */
- sizeof(query)); /* size of the buffer */
-
- /*
- * Send the query packet. If there is no name server
- * running on the target host, res_send() returns -1
- * and errno is ECONNREFUSED. First, clear out errno.
- */
- errno = 0;
- if((responseLen = res_send((char *)&query, /* the query */
- queryLen, /* true length*/
- (char *)&response, /*buffer */
- sizeof(response))) /*buf size*/
- < 0){ /* error */
- if(errno == ECONNREFUSED) { /* no server on the host */
- (void) fprintf(stderr,
- "There is no name server running on %s\n",
- nsList[nsNum]);
- } else { /* anything else: no response */
- (void) fprintf(stderr,
- "There was no response from %s\n",
- nsList[nsNum]);
- }
- continue; /* nsNum for-loop */
- }
-
- /*
- * Set up the pointers to parse the response.
- * We set up two pointers: one to the end of the message
- * (so we can test for overruns) and one to the question
- * section (which we'll move as we parse the response).
- */
- endOfMsg = response.buf + responseLen;
- cp = response.buf + sizeof(HEADER);
-
- /*
- * If the response reports an error, issue a message
- * and proceed to the next server in the list.
- */
- if(response.hdr.rcode != NOERROR){
- returnCodeError((int)response.hdr.rcode,
- nsList[nsNum]);
- continue; /* nsNum for-loop */
- }
-
- /*
- * Did we receive an authoritative response? Check the
- * authoritative answer bit. If the server isn't
- * authoritative, report it, and go on to the next server.
- */
- if(!response.hdr.aa){
- (void) fprintf(stderr,
- "%s is not authoritative for %s\n",
- nsList[nsNum], domain);
- continue; /* nsNum for-loop */
- }
-
- /*
- * The response should only contain one answer; if more,
- * report the error, and proceed to the next server.
- */
- if(ntohs(response.hdr.ancount) != 1){
- (void) fprintf(stderr,
- "%s: expected 1 answer, got %d\n",
- nsList[nsNum], ntohs(response.hdr.ancount));
- continue; /* nsNum for-loop */
- }
-
- /*
- * Skip the question section (we know what we asked,
- * don't we?). cp now points to the answer section.
- */
- cp += skipName(cp, endOfMsg) + QFIXEDSZ;
-
- /*
- * cp is now pointing at a resource record in the answer
- * section. Skip to the data portion of this record;
- * in the process, extract the type, class, etc.
- */
- cp += skipToData(cp, &type, &class, &ttl, &dlen, endOfMsg);
-
- /*
- * We asked for an SOA record; if we got something else,
- * report the error and proceed to the next server.
- */
- if (type != T_SOA) {
- (void) fprintf(stderr,
- "%s: expected answer type %d, got %d\n",
- nsList[nsNum], T_SOA, type);
- continue; /* nsNum for-loop */
- }
-
- /*
- * Skip the SOA origin and mail address, which we don't
- * care about. Both are standard "compressed names."
- */
- cp += skipName(cp, endOfMsg);
- cp += skipName(cp, endOfMsg);
-
- /* cp now points to the serial number; print it. */
- GETLONG(serial, cp);
- (void) printf("%s has serial number %d\n",
- nsList[nsNum], serial);
-
- } /* end of nsNum for-loop */
- }
-
- /****************************************************************
- * skipName -- This routine skips over a domain name. If the *
- * domain name expansion fails, it reports an error and *
- * exits. dn_skipname() is probably not on your manual *
- * page; it is similar to dn_expand() except that it just *
- * skips over the name. dn_skipname() is in res_comp.c if *
- * you need to find it. *
- ****************************************************************/
- int
- skipName(cp, endOfMsg)
- u_char *cp;
- u_char *endOfMsg;
- {
- int n;
-
- if((n = dn_skipname(cp, endOfMsg)) < 0){
- (void) fprintf(stderr, "dn_skipname failed\n");
- exit(1);
- }
- return(n);
- }
-
- /****************************************************************
- * skipToData -- This routine advances the cp pointer to the *
- * start of the resource record data portion. On the way, *
- * it fills in the type, class, ttl, and data length *
- ****************************************************************/
- int
- skipToData(cp, type, class, ttl, dlen, endOfMsg)
- u_char *cp;
- u_short *type;
- u_short *class;
- u_int32_t *ttl;
- u_short *dlen;
- u_char *endOfMsg;
- {
- u_char *tmp_cp = cp; /* temporary version of cp */
-
- /* Skip the domain name; it matches the name we looked up */
- tmp_cp += skipName(tmp_cp, endOfMsg);
-
- /*
- * Grab the type, class, and ttl. GETSHORT and GETLONG
- * are macros defined in arpa/nameser.h.
- */
- GETSHORT(*type, tmp_cp);
- GETSHORT(*class, tmp_cp);
- GETLONG(*ttl, tmp_cp);
- GETSHORT(*dlen, tmp_cp);
-
- return(tmp_cp - cp);
- }
-
- /****************************************************************
- * nsError -- Print an error message from h_errno for a failure *
- * looking up NS records. res_query() converts the DNS *
- * packet return code to a smaller list of errors and *
- * places the error value in h_errno. There is a routine *
- * called herror() for printing out strings from h_errno *
- * like perror() does for errno. Unfortunately, the *
- * herror() messages assume you are looking up address *
- * records for hosts. In this program, we are looking up *
- * NS records for domains, so we need our own list of error *
- * strings. *
- ****************************************************************/
- void
- nsError(error, domain)
- int error;
- char *domain;
- {
- switch(error){
- case HOST_NOT_FOUND:
- (void) fprintf(stderr, "Unknown domain: %s\n", domain);
- break;
- case NO_DATA:
- (void) fprintf(stderr, "No NS records for %s\n", domain);
- break;
- case TRY_AGAIN:
- (void) fprintf(stderr, "No response for NS query\n");
- break;
- default:
- (void) fprintf(stderr, "Unexpected error\n");
- break;
- }
- }
-
- /****************************************************************
- * returnCodeError -- print out an error message from a DNS *
- * response return code. *
- ****************************************************************/
- void
- returnCodeError(rcode, nameserver)
- int rcode;
- char *nameserver;
- {
- (void) fprintf(stderr, "%s: ", nameserver);
- switch(rcode){
- case FORMERR:
- (void) fprintf(stderr, "FORMERR response\n");
- break;
- case SERVFAIL:
- (void) fprintf(stderr, "SERVFAIL response\n");
- break;
- case NXDOMAIN:
- (void) fprintf(stderr, "NXDOMAIN response\n");
- break;
- case NOTIMP:
- (void) fprintf(stderr, "NOTIMP response\n");
- break;
- case REFUSED:
- (void) fprintf(stderr, "REFUSED response\n");
- break;
- default:
- (void) fprintf(stderr, "unexpected return code\n");
- break;
- }
- }
-